#include "zjson.h"

#define    Start                0x1<<1
#define    ObjectInitial        0x1<<2
#define    MemberKey            0x1<<3
#define    KeyValueDelimiter    0x1<<4
#define    MemberValue          0x1<<5
#define    MemberDelimiter      0x1<<6
#define    ObjectFinish         0x1<<7
#define    ArrayInitial         0x1<<8
#define    Element              0x1<<9
#define    ElementDelimiter     0x1<<10
#define    ArrayFinish          0x1<<11
#define    Finish               0x1<<12

/*
 * Log macro
 * What we can catch:
 * - JSON string
 * - current state
 * - current character
 * - current token start and end positions
 * - current line number and column number
 */
#define ERR(e) do{\
	put_err_token(); \
	printf("Unexpected char: '%c'\n" \
			"\tstatus [%s] \n" \
			"\tfunction %s:%d\n" \
			"\ttoken position:(%d,%d)\n", \
			*(e),StateStrings[_log2(state)-1],__FUNCTION__,__LINE__,line,column);\
	exit(EXIT_FAILURE);\
}while(0)

#define match(s, st) (s)&(st)
#define SET_TOKEN_START(ptr) token_start = (ptr)
#define SET_TOKEN_END(ptr) token_end = (ptr)
#define PEEK(ptr) (*(ptr))
#define EAT(ptr) do{SET_TOKEN_START(ptr);(ptr)++;column++;}while(0);

typedef int State;
typedef struct stack {
	jval *head;
	struct stack *next;
} stack;

static void put_err_token(void);

static void put_context(void);

static inline int is_xdigit(jchar ch);

static inline int is_digit(jchar ch);

static void stack_push(stack *st, jval *elem);

static jval *stack_pop(stack *st);

static jstr eat_key(const jchar **pp);

static jval *eat_string(const jchar **);

static jval *eat_num(const jchar **);

static jval *eat_true(const jchar **);

static jval *eat_false(const jchar **);

static jval *eat_null(const jchar **);

static inline int _log2(int n);

static const char *StateStrings[] = {
	"Start",
	"ObjectInitial",
	"MemberKey",
	"KeyValueDelimiter",
	"MemberValue",
	"MemberDelimiter",
	"ObjectFinish",
	"ArrayInitial",
	"Element",
	"ElementDelimiter",
	"ArrayFinish",
	"Finish"
};

static State state = Start;
static int line = 1, column = 1;
static const char *token_start = NULL, *token_end = NULL, *JSON = NULL;

static inline int _log2(int n) {
	int e = 0;
	while (n >>= 1) e++;
	return e;
}

static void put_context(void) {
	const char *ptr = JSON;
	int count = 0;
	while (count < line) {
		ptr++;
		if (*ptr == '\n')
			count++;
	}
}

static void put_err_token(void) {
	printf("Error Token:\n\t");
	const char *ptr = token_start, *ptr2 = token_start;
	while (ptr <= token_end) printf("%c", *ptr++);
	printf("\n\t");
	while (ptr2++ < token_end) printf(" ");
	printf("^");
	printf("\n\t");
}

static void literal_value_handler(const jchar **pp,
		jval *currentNode,
		jstr currentKey,
		jval *(*handle)(const jchar **)) {
	if (match(state, ArrayInitial | ElementDelimiter)) {
		state = Element;
		zJSON_set_arr_value(currentNode, handle(pp));
	} else if (match(state, KeyValueDelimiter)) {
		state = MemberValue;
		zJSON_set_key_value(currentNode, currentKey, handle(pp));
	} else
		ERR(*pp);
}

static void obj_arr_handler(const jchar **pp,
		vtype type,
		jval **pcurrentNode,
		stack *nodeStack,
		jchar *currentKey) {
	jval *newObjOrArray = zJSON_new_jval(type);
	if (match(state, Start)) {
		EAT(*pp);
	} else if (match(state, KeyValueDelimiter)) {
		zJSON_set_key_value(*pcurrentNode, currentKey, newObjOrArray);
		EAT(*pp);
	} else if (match(state, ArrayInitial | ElementDelimiter)) {
		zJSON_set_arr_value(*pcurrentNode, newObjOrArray);
		EAT(*pp);
	} else {
		ERR(*pp);
	}
	state = (type == OBJ) ? ObjectInitial : ArrayInitial;
	stack_push(nodeStack, *pcurrentNode);
	*pcurrentNode = newObjOrArray;
}

jval *zJSON_parse(const jchar *json) {
	JSON = json;
	const jchar *p = json;
	token_start = json;
	token_end = token_start;
	jval *currentNode = NULL;
	jchar *currentKey = NULL;
	stack *nodeStack = (stack *) malloc(sizeof(stack)); /* free() at this function's end */
	nodeStack->head = NULL;
	nodeStack->next = NULL;
	while (1) {
		switch (PEEK(p)) {
			case '{':
			case '[':	
				obj_arr_handler(&p,(*p == '{')?OBJ:ARR,&currentNode,nodeStack,currentKey);
				break;
			case '"': 
				if (match(state, ObjectInitial | MemberDelimiter)) {
					state = MemberKey;
					zJSON_set_key(currentNode, currentKey = eat_key(&p));
				} else
					literal_value_handler(&p, currentNode, currentKey, eat_string);
				break;
			case 't':
				literal_value_handler(&p, currentNode, currentKey, eat_true);
				break;
			case 'f':
				literal_value_handler(&p, currentNode, currentKey, eat_false);
				break;
			case 'n':
				literal_value_handler(&p, currentNode, currentKey, eat_null);
				break;
			case '0': case '1': case '2': case '3': case '4': 
			case '5': case '6': case '7': case '8': case '9':
			case '-': 
				literal_value_handler(&p, currentNode, currentKey, eat_num);
				break;
			case '}': 
			case ']': 
				if (match(state, ObjectInitial | MemberValue | ArrayInitial | Element)) {
					jval *t_currentNode;
					if ((t_currentNode = stack_pop(nodeStack))) {
						currentNode = t_currentNode;
						state = (currentNode->type == ARR) ? Element : MemberValue;
						EAT(p);
					} else {
						EAT(p);
						goto AUTOMATA_END;
					}
				} else
					ERR(p);
				break;
			case ',': 
				if (match(state, MemberValue)) {
					state = MemberDelimiter;
					EAT(p);
				} else if (match(state, Element)) {
					state = ElementDelimiter;
					EAT(p);
				} else
					ERR(p);
				break;
			case ':': 
				if (match(state, MemberKey)) {
					state = KeyValueDelimiter;
					EAT(p);
				} else
					ERR(p);
				break;
			case '\n':
				line++; 
				column = 1;
				/*NO break here*/
			case '\t':
			case ' ':
				EAT(p);
				break;
			case '\0':
				if (match(state, ArrayFinish | ObjectFinish))
					state = Finish;
				else
					ERR(p);
				goto AUTOMATA_END;
			default:
				ERR(p);
		}
	}
AUTOMATA_END:
	free(nodeStack);
	state = Start;
	return currentNode;
}

static jval *eat_literal(const jchar **pp, const char *str) {
	SET_TOKEN_START(*pp);
	const char *s = str;
	jval *v = NULL;
	while (*s) {
		if (**pp == *s) {
			(*pp)++;
			s++;
		} else {
			ERR(*pp);
		}
	}
	if (*str == 't') {
		v = zJSON_new_true();
	}
	if (*str == 'f') {
		v = zJSON_new_false();
	}
	if (*str == 'n') {
		v = zJSON_new_null();
	}
	SET_TOKEN_END(*pp);
	return v;
}

inline jval *eat_true(const jchar **pp) {
	return eat_literal(pp, "true");
}

inline jval *eat_false(const jchar **pp) {
	return eat_literal(pp, "false");
}

inline jval *eat_null(const jchar **pp) {
	return eat_literal(pp, "null");
}

#define __PREV(pp) (*(*pp-1))
#define __NEXT(pp) (*(*pp+1))

static unsigned parse_hex4(const char *str) {
	unsigned h = 0;
	if (*str >= '0' && *str <= '9')
		h += (*str) - '0';
	else if (*str >= 'A' && *str <= 'F')
		h += 10 + (*str) - 'A';
	else if (*str >= 'a' && *str <= 'f')
		h += 10 + (*str) - 'a';
	else
		return 0;
	h = h << 4;
	str++;
	if (*str >= '0' && *str <= '9')
		h += (*str) - '0';
	else if (*str >= 'A' && *str <= 'F')
		h += 10 + (*str) - 'A';
	else if (*str >= 'a' && *str <= 'f')
		h += 10 + (*str) - 'a';
	else
		return 0;
	h = h << 4;
	str++;
	if (*str >= '0' && *str <= '9')
		h += (*str) - '0';
	else if (*str >= 'A' && *str <= 'F')
		h += 10 + (*str) - 'A';
	else if (*str >= 'a' && *str <= 'f')
		h += 10 + (*str) - 'a';
	else
		return 0;
	h = h << 4;
	str++;
	if (*str >= '0' && *str <= '9')
		h += (*str) - '0';
	else if (*str >= 'A' && *str <= 'F')
		h += 10 + (*str) - 'A';
	else if (*str >= 'a' && *str <= 'f')
		h += 10 + (*str) - 'a';
	else
		return 0;
	return h;
}

static const unsigned char firstByteMark[7] = {0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC};

jstr eat_key(const jchar **pp) {
	SET_TOKEN_START(*pp);
	(*pp)++;/* skip the first '"'*/
	const jchar *start_pos = *pp,*end_pos;
	size_t len = 0,unicode_len;
	unsigned uc,uc2;

	while (*pp && !(*(*pp)++ == '\"' && __PREV(pp) != '\\') && ++len){
		if (**pp == '\\') (*pp)++;
	} /* Skip every escaped character, calculate the lenghth,
		 Each unicode char occupies 5 bytes */
	jstr str = malloc(len + 1);	/* free() at free_jpair() */
	end_pos = *pp;
	*pp = start_pos;

	char * ptr = str;
	while (*pp && !(**pp == '\"' && __PREV(pp) != '\\')){
		if (**pp != '\\') {
			*ptr++ = *((*pp)++);
		} else {
			switch (__NEXT(pp)) {
				case 'b': *ptr++ = '\b'; (*pp) += 2; break;
				case 'f': *ptr++ = '\f'; (*pp) += 2; break;
				case 'n': *ptr++ = '\n'; (*pp) += 2; break;
				case 'r': *ptr++ = '\r'; (*pp) += 2; break;
				case 't': *ptr++ = '\t'; (*pp) += 2; break;
				case '"': *ptr++ = '\"'; (*pp) += 2; break;
				case '/': *ptr++ =  '/'; (*pp) += 2; break;
				case '\\':*ptr++ = '\\'; (*pp) += 2; break;
				case 'u':
						  uc = parse_hex4((*pp)+2);
						  (*pp) += 6;/* Why ? */
						  if ((uc >= 0xDC00 && uc <= 0xDFFF) || uc == 0) { ERR((*pp)); } /* invalid unicode number */
						  if (uc >= 0xD800 && uc <= 0xDBFF) {
							  uc2 = parse_hex4((*pp)+2); 
							  (*pp) += 6;
							  if ((uc2 >= 0xDC00 && uc2 <= 0xDFFF) || uc2 == 0) { ERR((*pp)); } 
							  uc = 0x10000 + (((uc & 0x3FF) << 10) | (uc2 & 0x3FF));
						  }

						  unicode_len = 4;
						  if (uc < 0x80)
						      unicode_len = 1;
						  else if (uc < 0x800)
						      unicode_len = 2;
						  else if (uc < 0x10000)
						      unicode_len = 3;
						  ptr += unicode_len;
						  switch (unicode_len) {
						      case 4: *--ptr = ((uc | 0x80) & 0xBF); uc >>= 6;
						      case 3: *--ptr = ((uc | 0x80) & 0xBF); uc >>= 6;
						      case 2: *--ptr = ((uc | 0x80) & 0xBF); uc >>= 6;
						      case 1: *--ptr = ( uc | firstByteMark[unicode_len]);
						  }
						  ptr += unicode_len;
						  break;
				default:
						   ERR(*pp);
			}
		}
	}
	str[len] = 0;
	(*pp)++;/*skip the last '"'*/
	column += len;
	SET_TOKEN_END(*pp);
	return str;
}

jval *eat_string(const jchar **pp) {
	jval *strv = zJSON_new_jval(STR);
	strv->data.vstr = eat_key(pp);
	return strv;
}

static inline int is_digit(jchar ch) {
	return ((ch >= '0') && (ch <= '9')) || (ch == '.');
}
static inline int is_xdigit(jchar ch) {
	return ((ch >= '0') && (ch <= '9')) 
		|| ((ch >= 'A') && (ch <= 'F'))
		|| ((ch >= 'a') && (ch <= 'f'));
}

jval *eat_num(const jchar **pp) {
	//TODO
	SET_TOKEN_START(*pp);
	jchar buffer[41] = {0};
	jchar *ptr = buffer;
	int dbl_flag = 0;
	if (**pp == '-') *(ptr++) = *((*pp)++);
	if (**pp != '.') {
		while (is_digit(**pp)) { *(ptr++) = *((*pp)++); }
	} else {
		ERR(*pp);
	}
	ptr = buffer;
	while (*ptr) if(*ptr++ == '.')dbl_flag = 1;

	jval *intv = zJSON_new_jval(dbl_flag?DBL:NUM);
	intv->data.vdouble = atof(buffer);
	SET_TOKEN_END(*pp);
	return intv;
}

void stack_push(stack *st, jval *head) {
	stack *e = (stack *) malloc(sizeof(stack)); /* free at stack_pop() */
	e->head = head;
	e->next = st->next;
	st->next = e;
}

jval *stack_pop(stack *st) {
	if (st->next == NULL)
		return NULL;
	jval *head = st->next->head;
	stack *e = st->next;
	st->next = e->next;
	free(e);
	return head;
}

